package com.yuan.contract.service.impl;

import cn.common.exception.BusinessException;
import cn.common.utils.UserContextUtils;
import cn.constant.ContractConstant;
import cn.constant.TokenOrderConstant;
import cn.model.*;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.gson.JsonObject;
import com.yuan.common.redis.constant.CacheConstants;
import com.yuan.common.redis.lock.RedisDistributedLock;
import com.yuan.common.redis.service.RedisRepository;
import com.yuan.contract.mapper.*;
import com.yuan.contract.product.MatchProducer;
import com.yuan.contract.service.ContractOrderService;
import com.yuan.contract.service.WarehouseService;
import lombok.extern.slf4j.Slf4j;
import org.apache.logging.log4j.util.Strings;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.concurrent.ThreadLocalRandom;

@Slf4j
@Service
public class ContractOrderServiceImpl extends ServiceImpl<ContractOrderMapper, ContractOrder> implements ContractOrderService {
    @Autowired
    private RedisRepository redisRepository;
    @Autowired
    private ContractMulMapper contractMulMapper;
    @Autowired
    private LeverMapper leverMapper;
    @Autowired
    private BalanceMapper balanceMapper;
    @Autowired
    private ContractOrderMapper contractOrderMapper;
    @Autowired
    private WarehouseMapper warehouseMapper;
    @Autowired
    private WarehouseService warehouseService;
    @Autowired
    private MatchProducer matchProducer;
    @Override
    public boolean setContractOrder(ContractOrder contractOrder) {
        String memberId = UserContextUtils.getCurrentUserId();
        matchProducer.putCurrency(memberId);
        log.info("setContractOrder {} {}",contractOrder,memberId );
        if(contractOrder.getContractHands()==null||contractOrder.getContractHands().doubleValue() <= 0){
            throw new BusinessException("手数必须大于0");
        }
        if(Strings.isBlank(contractOrder.getPairsName())){
            throw new BusinessException("name 不能为空");
        }
        if (Strings.isEmpty(contractOrder.getCoinName())) {
            throw new BusinessException("CoinName名称不允许为空");
        }
        if(Strings.isBlank(contractOrder.getMainCur())){
            throw new BusinessException("MainCur不允许为空");
        }
        if(contractOrder.getTradeType()==null||Strings.isEmpty(contractOrder.getTradeType().getTradeType())){
            throw new BusinessException("类型不允许为空");
        }
        if(contractOrder.getPriceType() == null||Strings.isBlank(contractOrder.getPriceType().getPriceType())){
            throw new BusinessException("价格类型不允许为空");
        }
        if(Strings.isBlank(contractOrder.getContractMulId())){
            throw new BusinessException("配置不允许为空");
        }
        if (Strings.isBlank(contractOrder.getLeverId())) {
            throw new BusinessException("杠杆id不能为空");
        }
        if (contractOrder.getPrice()==null||contractOrder.getPrice().doubleValue() <= 0) {
            throw new BusinessException("价格必须大于0");
        }
        RedisDistributedLock redisDistributedLock =  new RedisDistributedLock(redisRepository.getRedisTemplate());
        log.info("memberId {} {}",memberId,contractOrder.getPairsName() );
        boolean lock_coin = redisDistributedLock.lock(CacheConstants.MEMBER_PAIRS_KEY + memberId + contractOrder.getPairsName(),
                5000, 50, 100);
        if(lock_coin){
            String value = redisRepository.get(CacheConstants.PRICE_HIG_LOW_KEY+contractOrder.getPairsName());
            JSONObject jo = JSONObject.parseObject(value);
            if(jo == null){
                JSONObject jsonRsult = new JSONObject();

                jsonRsult.put("higPrice", ThreadLocalRandom.current().nextDouble(10, 21));
                jsonRsult.put("lowPrice", ThreadLocalRandom.current().nextDouble(10, 21));
                jsonRsult.put("nowPrice", BigDecimal.valueOf(ThreadLocalRandom.current().nextDouble(10, 21)));
                jsonRsult.put("volume", System.currentTimeMillis());
                jsonRsult.put("updown", ThreadLocalRandom.current().nextDouble(10, 21));
                jsonRsult.put("open", ThreadLocalRandom.current().nextDouble(10, 21));
                redisRepository.set(CacheConstants.PRICE_HIG_LOW_KEY + contractOrder.getPairsName(), jsonRsult.toJSONString());
                throw new BusinessException("交易价格不存在");
            }
            BigDecimal nowPrice = new BigDecimal(jo.getBigDecimal("nowPrice").toString());
            ContractMul contractMul = contractMulMapper.selectById(contractOrder.getContractMulId());
            if(!contractMul.getPairsName().equals(contractOrder.getPairsName())){
                throw new BusinessException("交易对错误");
            }
            log.info("getContractMul: {}",contractMul);
            contractOrder.setMember(memberId);
            BigDecimal coinNum = contractMul.getContractMul().multiply(contractOrder.getContractHands());
            Lever lever = leverMapper.selectById(contractOrder.getLeverId());
            BigDecimal sumPrice = (contractMul.getContractMul().multiply(contractOrder.getContractHands().multiply(contractOrder.getPrice()))).divide(lever.getLever(),8, RoundingMode.HALF_UP);
            contractOrder.setCoinNum(coinNum);
            contractOrder.setMargin(sumPrice);
            contractOrder.setOrderState(TokenOrderConstant.Order_State.CREATE);
            contractOrder.setLeverNum(lever.getLever());
            contractOrder.setLeverDesc(lever.getLeverDesc());
            contractOrder.setIsContractHands(contractOrder.getContractHands());
            BigDecimal takeFee = openBalance(contractOrder,contractMul,sumPrice);
            contractOrder.setTakeFee(takeFee);
            if (contractOrder.getPriceType().equals(ContractConstant.Price_Type.CUSTOM_PRICE)) {
                Set<String> set = new HashSet<String>();
                if (contractOrder.getTradeType().equals(ContractConstant.Trade_Type.OPEN_UP)) {
                    set.addAll(redisRepository.zsetRangByScore(
                            CacheConstants.TOKEN_ORDER_MATCH_KEY + ContractConstant.Trade_Type.OPEN_DOWN
                                    + CacheConstants.SPLIT + contractOrder.getPairsName(),
                            0, contractOrder.getPrice().doubleValue()));

                } else {
                    set.addAll(redisRepository.zsetRevRangByScore(
                            CacheConstants.TOKEN_ORDER_MATCH_KEY + ContractConstant.Trade_Type.OPEN_UP
                                    + CacheConstants.SPLIT + contractOrder.getPairsName(),
                            contractOrder.getPrice().doubleValue(), Long.parseLong("99999999999")));
                }
                if (!set.isEmpty()) {
                    contractOrder.setOrderType(TokenOrderConstant.Order_Type.POSITIONS);
                    contractOrder.setPriceType(ContractConstant.Price_Type.MARKET_PRICE);
                } else {
                    contractOrder.setOrderType(TokenOrderConstant.Order_Type.ORDERS);
                    // 普通委托插入redis中
                    redisRepository.zsetAdd(
                            CacheConstants.TOKEN_ORDER_CUSTOM_KEY + contractOrder.getTradeType() + CacheConstants.SPLIT
                                    + contractOrder.getPairsName(),
                            JSONObject.toJSONString(contractOrder), contractOrder.getPrice().doubleValue());
                }
            } else {
                contractOrder.setOrderType(TokenOrderConstant.Order_Type.POSITIONS);
            }
            contractOrderMapper.insert(contractOrder);
            //发送消息

            redisDistributedLock.releaseLock(CacheConstants.MEMBER_PAIRS_KEY + memberId + contractOrder.getPairsName());

            if (contractOrder.getPriceType().equals(ContractConstant.Price_Type.MARKET_PRICE)) {
                QueryWrapper<Warehouse> wrapperWarehouse = new QueryWrapper<>();
                wrapperWarehouse.eq("pairs_name", contractOrder.getPairsName());
                wrapperWarehouse.eq("member", contractOrder.getMember());
                wrapperWarehouse.eq("trade_type", contractOrder.getTradeType());
                wrapperWarehouse.eq("state", TokenOrderConstant.Order_State.CREATE);
                Warehouse warehouse = warehouseMapper.selectOne(wrapperWarehouse);
                if (warehouse == null) {
                    // 个人组合订单
                    warehouse = new Warehouse();
                }
                warehouse.setState(TokenOrderConstant.Order_State.CREATE);
                warehouse.setPairsName(contractOrder.getPairsName());
                warehouse.setCoinName(contractOrder.getCoinName());
                warehouse.setMainCur(contractOrder.getMainCur());
                warehouse.setTradeType(contractOrder.getTradeType());
                // 持仓均价
                QueryWrapper<ContractOrder> wrapperOrder = new QueryWrapper<ContractOrder>();
                wrapperOrder.eq("pairs_name", contractOrder.getPairsName());
                wrapperOrder.eq("member", contractOrder.getMember());
                wrapperOrder.eq("trade_type", contractOrder.getTradeType());
                wrapperOrder.ne("order_state", TokenOrderConstant.Order_State.FINAL);
                wrapperOrder.eq("order_type", TokenOrderConstant.Order_Type.POSITIONS);
                List<ContractOrder> contractOrders = contractOrderMapper.selectList(wrapperOrder);
                BigDecimal sumTokenPrice = new BigDecimal("0");
                BigDecimal sumHands = new BigDecimal("0");
                BigDecimal levers = new BigDecimal("0");
                BigDecimal sumMargin = new BigDecimal("0");
                for (ContractOrder contractOrderInfo : contractOrders) {
                    sumTokenPrice = sumTokenPrice.add(contractOrderInfo.getPrice()
                            .multiply(contractOrderInfo.getIsContractHands()).multiply(contractMul.getContractMul()));
                    sumHands = sumHands.add(contractOrderInfo.getIsContractHands());
                    levers = levers.add(contractOrderInfo.getLeverNum());
                    sumMargin = sumMargin.add(contractOrderInfo.getMargin());
                }
                // 总保证金
                warehouse.setMargin(sumMargin);
                // 持仓均价
                BigDecimal avePrice = sumTokenPrice.divide(sumHands.multiply(contractMul.getContractMul()), 8,
                        BigDecimal.ROUND_HALF_UP);
                warehouse.setAvePrice(avePrice);
                // 仓位价值
                BigDecimal tokenPrice = contractMul.getContractMul()
                        .multiply(contractOrder.getContractHands().multiply(contractOrder.getPrice()));
                tokenPrice = tokenPrice.add(sumTokenPrice);
                warehouse.setTokenPrice(tokenPrice);
                // 仓位总数
                warehouse.setTokenNum(new BigDecimal(contractOrders.size()));
                // 可用仓位总数
                QueryWrapper<ContractOrder> isTokenNumWrapper = new QueryWrapper<ContractOrder>();
                isTokenNumWrapper.eq("pairs_name", contractOrder.getPairsName());
                isTokenNumWrapper.eq("member", contractOrder.getMember());
                isTokenNumWrapper.ge("order_state", TokenOrderConstant.Order_State.CREATE);
                warehouse.setIsTokenNum(new BigDecimal(contractOrderMapper.selectCount(isTokenNumWrapper)));

                warehouse.setHands(sumHands);
                warehouse.setOrders(new BigDecimal(contractOrders.size()));
                warehouse.setClosePrice(nowPrice);
                warehouse.setMember(contractOrder.getMember());
                if (contractOrder.getTradeType().equals(ContractConstant.Trade_Type.OPEN_UP)) {
                    // 未实现盈亏
                    BigDecimal subPrice = nowPrice.subtract(avePrice);
                    warehouse.setUnProfitLoss(subPrice.multiply(sumHands).multiply(contractMul.getContractMul()));
                    BigDecimal aveLevel = levers.divide(new BigDecimal(contractOrders.size()), 8, RoundingMode.HALF_UP);
                    warehouse.setAvgLevel(aveLevel);
                    // 收益
                    BigDecimal profit = subPrice.multiply(aveLevel).divide(avePrice, 8, RoundingMode.HALF_UP)
                            .divide(new BigDecimal(contractOrders.size()), 8, RoundingMode.HALF_UP);
                    warehouse.setProfit(profit);
                } else {
                    // 未实现盈亏
                    BigDecimal subPrice = avePrice.subtract(nowPrice);
                    warehouse.setUnProfitLoss(subPrice.multiply(sumHands).multiply(contractMul.getContractMul()));
                    BigDecimal aveLevel = levers.divide(new BigDecimal(contractOrders.size()), 8, RoundingMode.HALF_UP);
                    warehouse.setAvgLevel(aveLevel);
                    // 收益
                    BigDecimal profit = subPrice.multiply(aveLevel).divide(avePrice, 8, RoundingMode.HALF_UP)
                            .divide(new BigDecimal(contractOrders.size()), 8, RoundingMode.HALF_UP);
                    warehouse.setProfit(profit);
                }
                warehouseService.saveOrUpdate(warehouse);
            }
        }else {
            throw new BusinessException("请求频繁");
        }
        return true;
    }
    public BigDecimal openBalance(ContractOrder contractOrder,ContractMul contractMul,BigDecimal margin) {
        BigDecimal takeFee = BigDecimal.ZERO;
        RedisDistributedLock redisDistributedLock = new RedisDistributedLock(redisRepository.getRedisTemplate());
        boolean lock_coin = redisDistributedLock.lock(
                CacheConstants.MEMBER_BALANCE_COIN_KEY + CacheConstants.SPLIT + contractOrder.getMember(), 5000, 50,
                100);
        if(lock_coin){
            QueryWrapper<Balance> wrapperMain = new QueryWrapper<>();
            wrapperMain.eq("currency", contractOrder.getMainCur());
            wrapperMain.eq("user_id", contractOrder.getMember());
            Balance balance = balanceMapper.selectOne(wrapperMain);
            if(balance.getTokenBalance().compareTo(margin) <= 0){
                throw new BusinessException("保证金余额不足");
            }
            // 手续费
            if (contractOrder.getTradeType().equals(ContractConstant.Trade_Type.OPEN_UP)) {
                takeFee = contractOrder.getIsContractHands().multiply(contractMul.getContractMul())
                        .multiply(contractOrder.getPrice()).multiply(contractMul.getMakerFee());
            } else {
                takeFee = contractOrder.getIsContractHands().multiply(contractMul.getContractMul())
                        .multiply(contractOrder.getPrice()).multiply(contractMul.getTakerFee());
            }
            // 冻结用户余额，保证金
            balance.setTokenBalance(balance.getTokenBalance().subtract(margin).subtract(takeFee));
            balance.setTokenBlockedBalance(balance.getTokenBlockedBalance().add(margin));
            balanceMapper.updateById(balance);
            redisDistributedLock.releaseLock(
                    CacheConstants.MEMBER_BALANCE_COIN_KEY + CacheConstants.SPLIT + balance.getUserId());
        }else {
            openBalance(contractOrder, contractMul, margin);
        }
        return takeFee;
    }
}
